오늘 풀어본 문제는 백준의 16946번 문제1이다. 문제 풀이에 사용한 언어는 C++ 이다.
이 문제의 내용과 조건은 다음과 같다.
$N \times M$ 의 행렬로 표현되는 맵이 있다. 맵에서 $0$ 은 이동할 수 있는 곳을 나타내고, $1$ 은 이동할 수 없는 벽이 있는 곳을 나타낸다. 한 칸에서 다른 칸으로 이동하려면, 두 칸이 인접해야 한다. 두 칸이 변을 공유할 때, 인접하다고 한다.
각각의 벽에 대해서 다음을 구해보려고 한다.
한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.
첫째 줄에 $N$ $(1 \le N \le 1,000)$, $M$ $(1 \le M \le 1,000)$ 이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다.
맵의 형태로 정답을 출력한다. 원래 빈 칸인 곳은 $0$ 을 출력하고, 벽인 곳은 이동할 수 있는 칸의 개수를 $10$ 으로 나눈 나머지를 출력한다.
문제를 해결하기 위해 사용한 방식은 다음과 같다.
모든 빈 공간에서 DFS 를 진행하여 각 구역별 지점의 개수, 각 지점에 속한 구역의 번호를 알아낸다.
각 벽에서 주위의 빈 공간들의 구역의 번호를 알아내어 해당 구역들에 속한 지점의 개수를 모두 더한 뒤 $10$ 으로 나눈 나머지를 구한다.
코드는 다음과 같이 작성하였다.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using pll = pair<ll, ll>;
int dy[4] = {-1, 1, 0, 0};
int dx[4] = {0, 0, -1, 1};
bool isInRange(const pair<int, int>& pos, int N, int M);
int main(void) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int N, M;
cin >> N >> M;
vector<vector<bool>> grid(N, vector<bool>(M, false));
vector<vector<bool>> visited(N, vector<bool>(M, false));
vector<vector<int>> setNum(N, vector<int>(M, 0));
vector<int> pointCounts;
stack<pair<pair<int, int>, int>> s;
for (int i=0; i<N; i++) {
string input;
cin >> input;
for (int j=0; j<M; j++) {
if (input[j] == '1') {
grid[i][j] = true;
}
else {
s.emplace(make_pair(i, j), -1);
}
}
}
while (!s.empty()) {
auto temp = s.top();
s.pop();
pair<int, int> currPos = temp.first;
int currSet = temp.second;
if (!visited[currPos.first][currPos.second]) {
if (currSet == -1) {
currSet = pointCounts.size();
pointCounts.emplace_back(0);
}
visited[currPos.first][currPos.second] = true;
setNum[currPos.first][currPos.second] = currSet;
pointCounts[currSet]++;
for (int dir=0; dir<4; dir++) {
pair<int, int> next = make_pair(currPos.first + dy[dir], currPos.second + dx[dir]);
if (isInRange(next, N, M) && !grid[next.first][next.second]) {
s.emplace(next, currSet);
}
}
}
}
for (int i=0; i<N; i++) {
for (int j=0; j<M; j++) {
if (grid[i][j]) {
int reachable = 1;
vector<bool> checked(pointCounts.size(), false);
for (int dir=0; dir<4; dir++) {
pair<int, int> adj = make_pair(i + dy[dir], j + dx[dir]);
if (isInRange(adj, N, M) && !grid[adj.first][adj.second]) {
int currSet = setNum[adj.first][adj.second];
if (!checked[currSet]) {
reachable += pointCounts[currSet];
checked[currSet] = true;
}
}
}
cout << reachable % 10;
}
else {
cout << 0;
}
}
cout << "\n";
}
return 0;
}
bool isInRange(const pair<int, int>& pos, int N, int M) {
if (pos.first < 0 || pos.first > N - 1) {
return false;
}
else if (pos.second < 0 || pos.second > M - 1) {
return false;
}
else {
return true;
}
}
그러자 모든 테스트 케이스를 통과하고 ‘맞았습니다’가 나오는 것을 확인할 수 있었다.
탐색 문제는 이제 꽤 능숙해진 것 같다. 어떻게 짜야할지 생각이 안나도 필요한 코드를 저절로 치고 있는 스스로의 모습을 발견할 수 있었던 것이다!
다른 문제들도 이렇게 술술 풀 수 있었으면 좋겠다… 예를 들면 DP 라던가, DP 라던가, DP 라던가… 물론 그러려면 더 연습을 해야할 것 같긴 한다.
오늘의 PS는 여기까지!
1: https://www.acmicpc.net/problem/16946